package com.binarycat.dynamiclimit.limiter.policy;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import org.springframework.util.CollectionUtils;

import com.binarycat.dynamiclimit.annotation.DynamicLimit;
import com.binarycat.dynamiclimit.bean.DynamicLimitData;
import com.binarycat.dynamiclimit.bean.MethodMappingLimitData;
import com.binarycat.dynamiclimit.constant.CommonLimitConstants;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricRegistry;
import com.google.common.util.concurrent.RateLimiter;

/**
 * 抽象的方法级别限流策略
 * 
 * @author jjb
 * @date 2018/08/06
 */
public abstract class AbstractMethodLevelLimitPolicy extends AbstractLimitPolicy {

    protected static final double RATIO = 0.5;

    /**
     * 临时存放度量的qps信息
     */
    protected double metricQpsValueTem;

    /**
     * 临时存放度量的条件信息(如,度量对应的超时率或者异常率)
     */
    protected double metricConditionValueTem;

    /**
     * 以当前策略筛选出需要限流的方法对应的限流信息
     */
    protected MethodMappingLimitData limitTargetMethodMappingLimitData;
    /**
     * 以当前策略筛选出需要限流的方法对应的度量信息
     */
    protected Metric limitTargetMetric;

    /**
     * 开启限流(功能描述:何时开启限流,以及开启限流的操作)
     * 
     * @return true:已经开启.false:未开启
     */
    protected abstract boolean startLimit();

    /**
     * 降低限流阀值(功能描述:何时降低限流阀值,以及降低限流阀值的操作)
     * 
     * @return true:可以降低阀值,false:无需降低阀值
     */
    protected abstract boolean reduceLimitThreshold();

    /**
     * 关闭限流(功能描述:何时关闭限流,以及关闭限流的操作)
     * 
     * @return true:已关闭,false:未关闭
     */
    protected abstract boolean endLimit();

    /**
     * 升高限流阀值(功能描述:何时升高限流阀值,以及升高限流阀值的操作)
     * 
     * @return true:可以升高阀值,false:无需升高阀值
     */
    protected abstract boolean increaseLimitThreshold();

    @Override
    public void executeLimitPolicy() {
        // 开启限流
        if (startLimit()) {
            String policyClassName =
                METHOD_EFFECTIVE_LIMIT_POLICY.get(limitTargetMethodMappingLimitData.getMetricKey());
            // 该方法上一次的执行限流策略与当前的限流策略不一致,则当前限流策略不执行,避免两种策略相互干扰
            if (policyClassName != null && !policyClassName.equals(this.getClass().getName())) {
                return;
            } else {
                METHOD_EFFECTIVE_LIMIT_POLICY.put(limitTargetMethodMappingLimitData.getMetricKey(),
                    this.getClass().getName());
            }

            // 关闭限流
            if (endLimit()) {
                METHOD_EFFECTIVE_LIMIT_POLICY.remove(limitTargetMethodMappingLimitData.getMetricKey());
                return;
            }

            // 降低限流阀值
            if (!reduceLimitThreshold()) {
                // 升高限流阀值
                increaseLimitThreshold();
                METHOD_EFFECTIVE_LIMIT_POLICY.remove(limitTargetMethodMappingLimitData.getMetricKey());
            } else {
                if (limitTargetMethodMappingLimitData != null) {
                    RateLimiter rateLimiter = limitTargetMethodMappingLimitData.getRateLimiter();
                    int qpsProtectionValue = dynamicLimiterManager.getDynamicLimiterConfig().getQpsProtectionValue();
                    if (rateLimiter.getRate() <= qpsProtectionValue) {
                        METHOD_EFFECTIVE_LIMIT_POLICY.remove(limitTargetMethodMappingLimitData.getMetricKey());
                    }
                }
            }
        }
    }

    /**
     * 筛选出需要限流的目标方法对应的限流信息以及度量信息
     * 
     */
    protected void filterLimitTargetMethod() {
        filterLimitTargetMethodByExecuteResult();
    }

    private void filterLimitTargetMethodByExecuteResult() {
        String methodName = handlerMethod.toString();
        DynamicLimitData limitData = dynamicLimiterManager.getLimitData();
        Map<String, Metric> metricMap = limitData.getMetricRegistry().getMetrics();
        Map<String, MethodMappingLimitData> methodMappingLimitDataMap = limitData.getMethodMappingLimitDataMap();
        MethodMappingLimitData methodMappingLimitData = methodMappingLimitDataMap.get(methodName);
        limitTargetMethodMappingLimitData = methodMappingLimitData;

        String metricKey = CommonLimitConstants.METRIC_QPS;
        limitTargetMetric = limitData.getMetricRegistry().getMetrics().get(MetricRegistry.name(methodName, metricKey));

        DynamicLimit dynamicLimit = methodMappingLimitData.getDynamicLimitAnnotation();
        if (dynamicLimit.limitByIp()) {
            // 筛选出该方法中以ip进行分类,qps最大的度量信息
            List<Entry<String, Metric>> collect = metricMap.entrySet().stream().filter(a -> {
                boolean startsWith = a.getKey().startsWith(CommonLimitConstants.PREFIX_METRIC_KEY_IP);
                boolean contains = a.getKey().contains(methodName);
                boolean isMeter = a.getValue() instanceof Meter;
                if (startsWith && contains && isMeter) {
                    return true;
                }
                return false;
            }).sorted((a, b) -> {
                Meter bQps = (Meter)b.getValue();
                Meter aQps = (Meter)a.getValue();
                boolean res = bQps.getOneMinuteRate() > aQps.getOneMinuteRate();
                return res ? 1 : -1;
            }).limit(1).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(collect)) {
                return;
            }
            Entry<String, Metric> maxQpsmethodEntryByIp = collect.get(0);

            String maxQpsmethodnameWithIp = maxQpsmethodEntryByIp.getKey().replace(".".concat(metricKey), "");

            limitTargetMethodMappingLimitData = methodMappingLimitDataMap.get(maxQpsmethodnameWithIp);
            limitTargetMetric = maxQpsmethodEntryByIp.getValue();

        }
    }
}
